from .log import log_gui
from rect import Rect
from inertia import InertialMover
import gobject
import gtk
import gobject
import time

def sgn(n):
  """Returns:
  n < 0: -1
  n == 0: 0
  n > 0: 1"""
  return cmp(n, 0)

class GuiApp(gtk.DrawingArea):
  ENTER_FROM_RIGHT = -1
  ENTER_FROM_LEFT = 1
  def __init__(self, parent):
    gtk.DrawingArea.__init__(self)
    self._parent = parent
    self._screens = {}
    self._current = None
    self._replacements = []
    self._replacement = None
    self._callback = None
    self._timer = None
    self.add_events(gtk.gdk.BUTTON_PRESS_MASK |
                    gtk.gdk.BUTTON_RELEASE_MASK |
                    gtk.gdk.POINTER_MOTION_MASK |
                    gtk.gdk.STRUCTURE_MASK |
                    gtk.gdk.EXPOSURE_MASK)
    gtk.DrawingArea.connect(self, "destroy",self.OnUnrealized)
    gtk.DrawingArea.connect(self, "expose_event", self.OnExpose)
    gtk.DrawingArea.connect(self, "realize", self.OnRealize)
    gtk.DrawingArea.connect(self, "size_allocate", self.OnResize)
    gtk.DrawingArea.connect(self, "button_press_event", self.OnButtonDown)
    gtk.DrawingArea.connect(self, "button_release_event", self.OnButtonUp)
    gtk.DrawingArea.connect(self, "motion_notify_event", self.OnMouseMove)
    self._current_cache = None
    self._current_position = None
    self._current_source = None
    self._replacement_cache = None
    self._replacement_source = None
    self._replacement_position = None
    # Remember the last mouse move coord. If you get the same one twice in
    # a row without a button up/down inbetween, drop it.
    self._prev_move = None
    self.set_size_request(800, 480)
    parent.put(self, 0, 0)

  def OnUnrealized(self, widget=None, data=None):
    if self._current: self._current.OnUnrealized(widget, data)

  def OnExpose(self, widget=None, event=None, data=None):
    log_gui('** GuiApp.OnExpose, self=',self)
    if self._current:
      log_gui('  Calling current onexpose', self.window)
      self._current.OnExpose(widget, event, data)

  def OnRealize(self, widget=None, event=None, data=None):
    if self._current: self._current.OnRealize(widget, event, data)

  def OnResize(self, widget, event=None, data=None):
    if self._current: self._current.OnResize(widget, event, data)

  def OnButtonDown(self, container_widget, event, data=None):
    log_gui("App.OnButtonDown called", time.time())
    self._prev_move = None
    if self._current: self._current.OnButtonDown(container_widget, event, data)

  def OnButtonUp(self, widget, event, data=None):
    log_gui("App.OnButtonUp called", time.time())
    self._prev_move = None
    if self._current: self._current.OnButtonUp(widget, event, data)

  def OnMouseMove(self, widget, event, data=None):
    x, y, state = event.window.get_pointer()
    if self._prev_move == (x,y):
      return
    self._prev_move = (x, y)
    if self._current: self._current.OnMouseMove(widget, event, data)

  def AddScreen(self, name, screen):
    self._screens[name] = screen

  def UnlinkScreen(self, name):
    if name in self._screens:
      del self._screens[name]

  def SetCurrentScreen(self, name, entry, callback=None):
    if self._current == self._screens[name]:
      self.Paint()
      return True
    if not self._current:
      log_gui("No current screen")
      self.SetScreenActive(self._screens[name])
      return

    self._current.SetIgnoreClicks(True)
    self._screens[name].SetIgnoreClicks(True)
    self._replacements.append((name, entry, callback))
    if not self._timer:
      log_gui('Starting timer')
      self._current_cache = gtk.gdk.Pixmap(self.window, 800, 480, -1)
      self._current.Draw(self._current_cache)
      self._replacement_cache = gtk.gdk.Pixmap(self.window, 800, 480, -1)
      self.StartTimer()
      self._replacement.Draw(self._replacement_cache)

  def StartTimer(self):
    self.InitializeEntry()
    self._timer = gobject.timeout_add(17, self.OnTimer)

  def CancelTimer(self):
    gobject.source_remove(self._timer)
    self._timer = None

  def InitializeEntry(self):
    self._replacement = self._screens[self._replacements[0][0]]
    self._entry = self._replacements[0][1]
    self._callback = self._replacements[0][2]
    self._replacements = self._replacements[1:]
    self._mover = InertialMover(0, self._entry * 300.0)
    self._mover.AbsoluteMove(800, time.time())
    self._offset = 0

  def OnTimer(self):
    more = self.Update()
    if not more:
      self._timer = None
    return more

  def MoveScreensBy(self, max_dx):
    orig_offset = self._offset
    self._offset += max_dx;
    if abs(self._offset) > 800:
      self._offset = sgn(self._offset) * 800
    if max_dx > 0:
      # Entering from the left. Show the left-hand side of current. Show the
      # right hand side of replacement.
      log_gui("Replacement:")
      self._replacement_position = (0, 0)
      self._replacement_source = Rect((
        800 - self._offset,
        0,
        self._offset,
        self._replacement.rect.height))
      self._current_position = (self._offset, 0)
      self._current_source = Rect((
        0,
        0,
        800 - self._offset,
        self._current.rect.height))
    else:
      # Entering from the right. Show the right-hand side of current. Show the
      # left-hand side of replacement.
      log_gui("Replacement from the right:",self._offset)
      self._replacement_position = (self._offset + 800, 0)
      self._replacement_source = Rect((
          0,
          0,
          -self._offset,
          self._replacement.rect.height))
      self._current_position = (0, 0)
      self._current_source = Rect((
          -self._offset,
          0,
          800 + self._offset,
          self._current.rect.height))
    return self._offset - orig_offset

  def Paint(self):
    self.window.begin_paint_rect((0, 0, 800, 480))
    self._current.Draw()
    self.window.end_paint()

  def Update(self):
    log_gui('Update')
    speed = 0
    while abs(speed) < 1:
      speed = self._mover.UpdateSpeed(time.time())
    log_gui('  speed=',speed)
    dx = self.MoveScreensBy(int(speed))
    log_gui('  dx=',dx)

    # Paint both.
    self.window.begin_paint_rect((0, 0, 800, 480))
    gc = gtk.gdk.GC(self.window)

    self.window.draw_drawable(
        gc,
        self._current_cache,
        self._current_source.left,
        self._current_source.top,
        self._current_position[0],
        self._current_position[1],
        self._current_source.width,
        self._current_source.height)

    self.window.draw_drawable(
        gc,
        self._replacement_cache,
        self._replacement_source.left,
        self._replacement_source.top,
        self._replacement_position[0],
        self._replacement_position[1],
        self._replacement_source.width,
        self._replacement_source.height)
    self.window.end_paint()

    if not dx:
      log_gui('  done...')
      if self._replacements:
        log_gui('    Bringing in the next screen.')
        # Start bringing in the next screen in the queue.
        self.InitializeEntry()
        return True
      else:
        # Done.
        log_gui('    All done.')
        self.CancelTimer()
        self.SetScreenActive(self._replacement)
        self._replacement = None
        self._callback = None
        self._entry = None
        self._current_cache = None
        self._replacement_cache = None
        return False
    else:
      # Normal case: more to go.
      return True

  def SetScreenActive(self, screen):
    if self._callback:
      self._callback()
    self._current = screen
    self._current.SetIgnoreClicks(False)
    self._current.SetClipRect(None)
    self._current.Paint()
